package fileHandling;

import java.util.HashMap;

import logic.nodes.nodeSettings.Settings;

import settings.GraphicSettings;

import com.jme.image.Texture;
import com.jme.scene.TriMesh;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.MaterialState.MaterialFace;
import com.jme.scene.state.RenderState.StateType;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;

import fileHandling.language.options.OptionValues;

/**
 * Provides static methods for {@link Texture}-loading and updating textures and texture-filters.
 * 
 * @author Wasserleiche
 */
public class TextureLoader {
	
	public static final String TEX_PATH = "data/textures/";
	public static final String OBJECT_PATH = TEX_PATH + "objects/";
	public static final String QUAL_PATH = OBJECT_PATH + "qualities/";
	public static final String PROP_PATH = OBJECT_PATH + "properties/";
	
	public static HashMap<String, TextureSettings> textureSettings = new HashMap<String, TextureSettings>();
	
	/**
	 * Creates a new {@link TextureState} from the given path. If there is any error the 
	 * current {@link Game} will be canceled immediately using InitGame.killGame().
	 * @param path The path to the {@link Texture} to be loaded.
	 * @return A new {@link TextureState} with the loaded {@link Texture}.
	 */
	public static TextureState getTextureState(String path) {
		TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
		
		Texture t = loadTexture(path);
		if(t == null) return null;
		
		ts.setTexture(t, 0);
		return ts;
	}
	
	private static Texture loadTexture(String texPath) {
		Texture.MinificationFilter nearFilter = GraphicSettings.get().getMinificationFilter();
		Texture.MagnificationFilter farFilter = Texture.MagnificationFilter.Bilinear;
		float anisotropy = GraphicSettings.get().getAnisotrop();
		
		return TextureManager.loadTexture(texPath, nearFilter, farFilter, anisotropy, true);
	}
	
	public static void addSpecularMap(String path, TextureState ts, int qualityDrop) {
		String normalQual = getDropedQuality(GraphicSettings.get().getNormalQuality(), qualityDrop);
		String specQual = getDropedQuality(GraphicSettings.get().getSpecularQuality(), qualityDrop);
		String heightQual = getDropedQuality(GraphicSettings.get().getBumpQuality(), qualityDrop);
		
		Texture normal = loadTexture(path + "normal/" + normalQual + ".png");
		Texture specular = loadTexture(path + "specular/" + specQual + ".png");
		Texture height = loadTexture(path + "height/" + heightQual + ".png");
		ts.setTexture(normal, 1);
		ts.setTexture(specular, 2);
		ts.setTexture(height, 3);
	}
	
	private static String getDropedQuality(String initQual, int drop) {
		String qual = initQual;
		for(int i = 0; i < drop; i++) {
			String oldQual = qual;
			qual = ModelImporter.nextLevel(qual, true);
			if(qual == null) {
				qual = oldQual;
				break;
			}
		}
		return qual;
	}
	
	public static void updateTextureQuality(TriMesh triMesh, String quality) {
		TextureState ts = (TextureState)triMesh.getRenderState(StateType.Texture);
		if(ts != null) {
			String loc = ts.getTexture().getImageLocation();
			int slash = loc.lastIndexOf("/");
			int dot = loc.lastIndexOf(".");
			String texName = loc.substring(slash, dot);
			
			ts = getTextureState(QUAL_PATH + quality + "/" + texName + ".png");
			triMesh.setRenderState(ts);
			
			triMesh.clearRenderState(StateType.GLSLShaderObjects);
			boolean isHighEnough = !quality.startsWith("lod");
			if(isHighEnough && GraphicSettings.get().getShaderQualityString().equals(OptionValues.High.toString())) {
				int qualDrop = 0;
				String texQual = GraphicSettings.get().getTextureQuality();
				
				while(!texQual.equals(quality)) {
					qualDrop++;
					texQual = ModelImporter.nextLevel(texQual, true);
				}
				
				addSpecularMap(PROP_PATH + texName + "/", ts, qualDrop);
				triMesh.setRenderState(ShaderLoader.getSpecularShader());
			}
			
			triMesh.setRenderState(getMaterialState(texName));
			triMesh.updateRenderState();
		}
	}
	
	private static MaterialState getMaterialState(String texture) {
		TextureSettings settings = textureSettings.get(texture);
		if(settings == null) {
			Settings newSettings = SettingsLoader.loadSettings(PROP_PATH + texture + "/" + "textureSettings.xml");
			settings = new TextureSettings(newSettings);
			textureSettings.put(texture, settings);
		}
		
		MaterialState ms = DisplaySystem.getDisplaySystem().getRenderer().createMaterialState();
		ms.setMaterialFace(MaterialFace.Front);
		ms.setEnabled(true);
		ms.setAmbient(settings.getAmbientColor());
		ms.setDiffuse(settings.getDiffuseColor());
		ms.setSpecular(settings.getSpecularColor());
		ms.setShininess(settings.getShininess());
		
		return ms;
	}
	
	/**
	 * Updates the magnification-filter, the minification-filter and the anisotropic-filter of the 
	 * given {@link Texture}.
	 * @param tex The {@link Texture} whose filters has to be updated.
	 */
	public static void upateTextureFilter(Texture tex) {
		tex.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
		tex.setMinificationFilter(GraphicSettings.get().getMinificationFilter());
		tex.setAnisotropicFilterPercent(GraphicSettings.get().getAnisotrop());
	}
}